﻿
using Microsoft.Xna.Framework.Graphics;
using System;
using Microsoft.Xna.Framework;
//using Arcane.Xna.Presentation.Properties;


using System.Threading;
using System.Collections.Generic;
using System.Runtime.InteropServices;

using FXnaGraphics;


namespace FXnaGraphics.WPF
{
    public class FGameDeviceManager : IGraphicsDeviceService,IGraphicsDeviceManager, IDisposable
    {

        #region 字段

        private GraphicsDevice device;

        public GraphicsDevice GraphicsDevice
        {
            get { return device; }
           
        }

        private static readonly TimeSpan deviceLostSleepTime = TimeSpan.FromMilliseconds(50.0);
        private WPFCanvas game;

        private bool inDeviceTransition;//正在创建device


        PresentationParameters parameters;

        // Keep track of how many controls are sharing the singletonInstance.
        static int referenceCount;


        #endregion

        #region 事件

        // IGraphicsDeviceService events.
        public event EventHandler<EventArgs> DeviceCreated;
        public event EventHandler<EventArgs> DeviceDisposing;
        public event EventHandler<EventArgs> DeviceReset;
        public event EventHandler<EventArgs> DeviceResetting;

        public event EventHandler Disposed;


        #endregion




        public FGameDeviceManager(WPFCanvas game)
        {
 
             if (game == null)
             {
                 throw new ArgumentNullException("game", Resource.GameCannotBeNull);
             }
             this.game = game;
             if (game.Services.GetService(typeof(IGraphicsDeviceManager)) != null)
             {
                 throw new ArgumentException(Resource.GraphicsDeviceManagerAlreadyPresent);
             }




             game.Services.AddService(typeof(IGraphicsDeviceManager), this);



             game.Services.AddService(typeof(IGraphicsDeviceService), this);

             game.Window.SizeChanged += new System.Windows.SizeChangedEventHandler(this.GameWindowClientSizeChanged);
           
        }



        void GameWindowClientSizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
        {
            if (game.IsActive)
            {
                if (!this.inDeviceTransition && ((this.game.Window.ActualHeight != 0) || (this.game.Window.ActualWidth != 0)))
                {


                   


                    
                    this.ChangeDevice(false);
                }
            }
        }


        private void ChangeDevice(bool forceCreate)
        {
            if (this.game == null)
            {
                throw new InvalidOperationException(Resource.GraphicsComponentNotAttachedToGame);
            }
          
            this.inDeviceTransition = true;
            string screenDeviceName = this.game.Window.Title;
            int width = (int)this.game.Window.ActualWidth;
            int height = (int)this.game.Window.ActualHeight;


           
            try
            {
               
                
                
                if (!forceCreate && (this.device != null))
                {


                    this.ResetDevice(width, height);

                }

                else
                {

                    this.CreateDevice();

                  
 

                }
                
               
            }
            finally
            {
              
                this.inDeviceTransition = false;
            }
        }

        private void CreateDevice()
        {

            if (this.device != null)
            {
                this.device.Dispose();
                this.device = null;
            }

          


            IntPtr handle = new System.Windows.Interop.WindowInteropHelper(this.game.Window).Handle;


            int width = (int)this.game.Window.ActualWidth;
            int height = (int)this.game.Window.ActualHeight;



            parameters = new PresentationParameters();

            parameters.BackBufferWidth = Math.Max(width, 1);
            parameters.BackBufferHeight = Math.Max(height, 1);
            parameters.BackBufferFormat = SurfaceFormat.Color;
            parameters.DepthStencilFormat = DepthFormat.Depth24;
            parameters.DeviceWindowHandle = handle;
            parameters.PresentationInterval = PresentInterval.Immediate;
            parameters.IsFullScreen = false;

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter,
                                                GraphicsProfile.HiDef,
                                                parameters);




            this.device.DeviceLost += new EventHandler<EventArgs>(HandleDeviceLost);
            this.device.DeviceReset += new EventHandler<EventArgs>(HandleDeviceReset);
            this.device.DeviceResetting += new EventHandler<EventArgs>(HandleDeviceResetting);
            this.device.Disposing += new EventHandler<EventArgs>(HandleDisposing);



            if (DeviceCreated!=null)
            {
                DeviceCreated(this,EventArgs.Empty);
                
            }
 

        }


        private void HandleDeviceLost(object sender, EventArgs e)
        {
            
        }

        private void HandleDeviceReset(object sender, EventArgs e)
        {
            this.DeviceReset(this, EventArgs.Empty);
        }

        private void HandleDeviceResetting(object sender, EventArgs e)
        {
            this.DeviceResetting(this, EventArgs.Empty);
        }

        private void HandleDisposing(object sender, EventArgs e)
        {
            this.DeviceDisposing(this, EventArgs.Empty);

        }



        /// <summary>
        /// Resets the graphics device to whichever is bigger out of the specified
        /// resolution or its current size. This behavior means the device will
        /// demand-grow to the largest of all its GraphicsDeviceControl clients.
        /// </summary>
        public void ResetDevice(int width, int height)
        {
            if (DeviceResetting != null)
                DeviceResetting(this, EventArgs.Empty);

            parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth, width);
            parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight, height);

            device.Reset(parameters);

            if (DeviceReset != null)
                DeviceReset(this, EventArgs.Empty);
        }



        void IDisposable.Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.game != null)
                {
                    if (this.game.Services.GetService(typeof(IGraphicsDeviceService)) == this)
                    {
                        this.game.Services.RemoveService(typeof(IGraphicsDeviceService));
                    }

                    this.game.Window.SizeChanged -= new System.Windows.SizeChangedEventHandler(this.GameWindowClientSizeChanged);
               
                }
                if (this.device != null)
                {
                    this.Release(true);

                    //this.device.Dispose();
                    //this.device = null;
                }
                if (this.Disposed != null)
                {
                    this.Disposed(this, EventArgs.Empty);
                }
            }
        }


        /// <summary>
        /// Releases a reference to the singleton instance.
        /// </summary>
        public void Release(bool disposing)
        {
          
                // If this is the last control to finish using the
                // device, we should dispose the singleton instance.
                if (disposing)
                {
                    if (DeviceDisposing != null)
                        DeviceDisposing(this, EventArgs.Empty);

                    device.Dispose();
                }

                device = null;
            
        }






        bool beginDrawOk = false;

        bool IGraphicsDeviceManager.BeginDraw()
        {

            //需要在每次之前，检查

            if (!this.EnsureDevice())
            {
                return false;
            }


            this.beginDrawOk = true;
            return true;


        }

        void IGraphicsDeviceManager.CreateDevice()
        {
            this.ChangeDevice(true);
        }

        void IGraphicsDeviceManager.EndDraw()
        {

            if (this.beginDrawOk&&this.device!=null)
            {
                device.Present();
                
            }

            
        }


        private bool EnsureDevice()
        {
            if (this.device == null)
            {
                return false;
            }
            return true;
           
        }


    }
}
